//4. void void 的含义是空，即：函数不返回任何值，调⽤者也不应依赖其返回值进⾏任何操作！
//1. void 通常⽤于函数返回值声明\
function logMessage(msg: string): void {
    console.log(msg)
}
logMessage('你好')
//注意：编码者没有编写 return 指定函数返回值，所以 logMessage 函数是没有显式
// 返回值的，但会有⼀个隐式返回值 ，是 undefined ，虽然函数返回类型为 void ，但
// 也是可以接受 undefined 的，简单记： undefined 是 void 可以接受的⼀种“空”。

//2. 以下写法均符合规范
// ⽆警告
function logMessage2(msg: string): void {
    console.log(msg)
}
// ⽆警告
function logMessage3(msg: string): void {
    console.log(msg)
    return;
}
// ⽆警告
function logMessage4(msg: string): void {
    console.log(msg)
    return undefined
}

// 3. 那限制函数返回值时，是不是 undefined 和 void 就没区别呢？—— 有区别。因为还有
// 这句话 ：【返回值类型为 void 的函数，调⽤者不应依赖其返回值进⾏任何操作！】对⽐下
// ⾯两段代码：
function voidDemo1(): void {

}
const void1 = voidDemo1()
// if (void1) {   // 此⾏报错：⽆法测试 "void" 类型的表达式的真实性


// }

function voidDemo2(): undefined {

}
const void2 = voidDemo2()
if (void2) { // 此⾏⽆警告

}
// 理解 void 与 undefined
// void 是⼀个⼴泛的概念，⽤来表达“空”，⽽ undefined 则是这种“空”的具体
// 实现。
// 因此可以说 undefined 是 void 能接受的⼀种“空”的状态。
// 也可以理解为： void 包含 undefined ，但 void 所表达的语义超越了 undefi
// ned ， void 是⼀种意图上的约定，⽽不仅仅是特定值的限制。 

// 总结：
// 如果⼀个函数返回类型为 void ，那么：
// 1. 从语法上讲：函数是可以返回 undefined 的，⾄于显式返回，还是隐式返回，这⽆
// 所谓！
// 2. 从语义上讲：函数调⽤者不应关⼼函数返回的值，也不应依赖返回值进⾏任何操作！
// 即使我们知道它返回了 undefined 。





// 5. object   关于 object 与 Object ，直接说结论：实际开发中⽤的相对较少，因为范围太⼤了。
//object（⼩写）
//     object （⼩写）的含义是：所有⾮原始类型，可存储：对象、函数、数组等，由于限制的范围⽐较宽泛，在实际开发中使⽤的相对较少。
let obj_a: object //a的值可以是任何【⾮原始类型】，包括：对象、函数、数组等

// 以下代码，是将【⾮原始类型】赋给a，所以均符合要求
obj_a = {}
obj_a = { name: '张三' }
obj_a = [1, 3, 5, 7, 9]
obj_a = function () { }
obj_a = new String('123')
class Person { }
obj_a = new Person()

// 以下代码，是将【原始类型】赋给a，有警告
// obj_a = 1 // 警告：不能将类型“number”分配给类型“object”
// obj_a = true // 警告：不能将类型“boolean”分配给类型“object”
// obj_a = '你好' // 警告：不能将类型“string”分配给类型“object”
// obj_a = null // 警告：不能将类型“null”分配给类型“object”
// obj_a = undefined // 警告：不能将类型“undefined”分配给类型“object”

//Object（⼤写）
//   官⽅描述：所有可以调⽤ Object ⽅法的类型。
//   简单记忆：除了 undefined 和 null 的任何值。
//   由于限制的范围实在太⼤了！所以实际开发中使⽤频率极低。
let obj_b: Object //b的值必须是Object的实例对象（除去undefined和null的任何值）

// 以下代码，均⽆警告，因为给a赋的值，都是Object的实例对象
obj_b = {}
obj_b = { name: '张三' }
obj_b = [1, 3, 5, 7, 9]
obj_b = function () { }
obj_b = new String('123')
class Person_b { }
obj_b = new Person_b()
obj_b = 1 // 1不是Object的实例对象，但其包装对象是Object的实例
obj_b = true // truue不是Object的实例对象，但其包装对象是Object的实例
obj_b = '你好' // “你好”不是Object的实例对象，但其包装对象是Object的实例

// 以下代码均有警告
// obj_b = null // 警告：不能将类型“null”分配给类型“Object”
// obj_b = undefined // 警告：不能将类型“undefined”分配给类型“Object”


// 声明对象类型
//      1. 实际开发中，限制⼀般对象，通常使⽤以下形式
//         限制person1对象必须有name属性，age为可选属性
let person1: { name: string, age?: number }
//         含义同上，也能⽤分号做分隔
let person2: { name: string; age?: number }
//         含义同上，也能⽤换⾏做分隔
let person3: {
    name: string
    age?: number
    // [ket: string]: any   //key为string类型，值为任意类型
}
//         如下赋值均可以
person1 = { name: "tom", age: 12 }
person2 = { name: "tom", age: 13 }
person3 = { name: "tom", age: 14 }

//         如下赋值不合法，因为person3的类型限制中，没有对gender属性的说明
person3 = { name: '王五', age: 18 }
// console.log(person3, person3.age, person3.name)

//    2. 索引签名： 允许定义对象可以具有任意数量的属性，这些属性的键和类型是可变的，常⽤于：描述类型不确定的属性，（具有动态属性的对象）。
let person4: {
    name: string
    age?: number
    [key: string]: any // 索引签名，完全可以不⽤key这个单词，换成其他的也可以
}
// 赋值合法
person4 = {
    name: '张三',
    age: 18,
    gender: '男'
}
// console.log(person4)


// 声明函数类型
let count: (x: number, y: number) => number

count = function (a, b) {
    return a + b
}
// function count_a(a: number, b: number) { return a + b }
// count = count_a
// 备注：
// TypeScript 中的 => 在函数类型声明时表示函数类型，描述其参数类型和返回类
// 型。
// JavaScript 中的 => 是⼀种定义函数的语法，是具体的函数实现。
// 函数类型声明还可以使⽤：接⼝、⾃定义类型等⽅式，下⽂中会详细讲解。


//声明数组类型
let arr1: string[]
let arr2: Array<string>
arr1 = ["x", "y", "z"]
arr2 = ["x", "y", "z"]
console.log(arr1, arr2)
//备注：上述代码中的 Array<string> 属于泛型，下⽂会详细讲解。
